--[[
* MininoteLib 2.0
* File: <AddOn>\MininoteLib\MininoteLib.lua
* $Revision: 135 $
* $Date: 2007-01-06 13:13:52 +0000 (Sat, 06 Jan 2007) $
* 
* Copyright (c) 2006-2007, Mery (Moritz Metelmann)
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 
*    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*    * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]--

if (not MininoteLib) then MininoteLib = {}; end;
if (not MininoteLib.XML) then MininoteLib.XML = {}; end;
if (not MininoteLib.Event) then MininoteLib.Event = {}; end;
if (not MininoteLib.registeredTables) then MininoteLib.registeredTables = {}; end;
if (not MininoteLib.Conversion) then MininoteLib.Conversion = {}; end;

MininoteLib.MAX_NOTES_PER_FRAME = 10;
MininoteLib.MIN_TIME_BETWEEN_CYCLES = 0.035; -- this means 28.57 times per second, I don't have a such high framerate lol
MininoteLib.MAX_ICON_MININOTE_SIZE = 24;
MininoteLib.PATH = "Interface\\AddOns\\MapNotesMininote\\MininoteLib\\";
MininoteLib.NUM_POIS = 0;

MininoteLib.minimapMask = MininoteLib.minimapMask or "DEFAULT";

local start_time, count_table, count_subtable;
local next_poi = 1;
MininoteLib.OnUpdate = function()
	local player_x, player_y, continentID = MapLib.GetPlayerContinentPosition();
	if (player_x == 0 and player_y == 0) then 
		if ( MininoteLib.NUM_POIS > 0 and MininoteLibPOI1:IsShown() ) then
			-- hide all POI's
			for i = 1, MininoteLib.NUM_POIS do
				getglobal("MininoteLibPOI"..i):Hide();
			end;
			-- reset to start so that the next cycle begins from the beginning
			next_poi = 1;
			count_table = 1;
			count_subtable = 1;
		end;
		return;
	end;
	local reg_tables = MininoteLib.registeredTables;
	local count_poi = 0;
	local cycle_restarted, cycle_complete;
	local rad_angle;
	local currentZoom = Minimap:GetZoom();
	-- this code finishes / resumes the last cycle:
	while (not cycle_complete) do
		while (count_table and count_table <= table.maxn(reg_tables)) do
			while (count_subtable <= table.maxn(reg_tables[count_table])) do
				local note = reg_tables[count_table][count_subtable];
				-- DevTools_Dump("next_poi:"..next_poi);
				-- create a new frame if needed:
				if (next_poi > MininoteLib.NUM_POIS) then MininoteLib.XML.CreatePOI(); end;
				local poi = getglobal("MininoteLibPOI"..next_poi);
				-- calculate the positon on the minimap
				-- note.title, note.icon, note.x, note.y
				local delta_x = (note.x - player_x) * MininoteLib.Conversion[continentID][currentZoom].xscale * (MininoteLib.isInside and MininoteLib.Conversion["isInside"][currentZoom] or 1);
				local delta_y = (note.y - player_y) * MininoteLib.Conversion[continentID][currentZoom].yscale * (MininoteLib.isInside and MininoteLib.Conversion["isInside"][currentZoom] or 1);
				-- DevTools_Dump("player_x:"..player_x.."note.x:"..note.x.."delta_x:"..delta_x);
				-- check it against the max bounderies function
				rad_angle = nil;
				if (MininoteLib.minimapMask == "DEFAULT") then
					if ( sqrt( (delta_x * delta_x) + (delta_y * delta_y) ) > 66 - math.min(note.icon.size/2, 12) ) then
						rad_angle = math.atan2(delta_y, delta_x);
						delta_x = math.cos(rad_angle) * 58;
						delta_y = math.sin(rad_angle) * 58;
					end;
				elseif (MininoteLib.minimapMask == "SQUARE") then
					if ( math.abs(delta_x) > 66 - math.min(note.icon.size/2, 12) or math.abs(delta_y) > 66 - math.min(note.icon.size/2, 12)) then
						rad_angle = math.atan2(delta_y, delta_x);
						-- some good function... LOL I'm too lazy to think of a clean way
						if ( delta_x > 0 and delta_x >= math.abs(delta_y) ) then
							delta_y = (delta_y / delta_x) * 58;
							delta_x = 58;
						elseif ( delta_y > 0 and delta_y > math.abs(delta_x) ) then
							delta_x = (delta_x / delta_y) * 58;
							delta_y = 58;
						elseif ( delta_x < 0 and -delta_x >= math.abs(delta_y) ) then
							delta_y = -(delta_y / delta_x) * 58;
							delta_x = -58;
						else --if ( delta_y < 0 and -delta_y > math.abs(delta_x) ) then -- can only be the last case
							delta_x = -(delta_x / delta_y) * 58;
							delta_y = -58;
						end;
					end;
				elseif (MininoteLib.minimapMask == "CORNER") then
					if ( (math.abs(delta_x) > 66 - math.min(note.icon.size/2, 12) or math.abs(delta_y) > 66 - math.min(note.icon.size/2, 12)) or (delta_x<0 and delta_y>0 and sqrt( (delta_x * delta_x) + (delta_y * delta_y) ) > 66 - math.min(note.icon.size/2, 12)) ) then
						rad_angle = math.atan2(delta_y, delta_x);
						-- some good function... LOL I'm too lazy to think of a clean way
						if ( delta_x > 0 and delta_x >= math.abs(delta_y) ) then
							delta_y = (delta_y / delta_x) * 58;
							delta_x = 58;
						elseif ( delta_y > 0 and delta_x > 0 and delta_y > delta_x ) then
							delta_x = (delta_x / delta_y) * 58;
							delta_y = 58;
						elseif ( delta_x < 0 and delta_y < 0 and delta_x <= delta_y ) then
							delta_y = -(delta_y / delta_x) * 58;
							delta_x = -58;
						elseif ( delta_y < 0 and -delta_y > math.abs(delta_x) ) then
							delta_x = -(delta_x / delta_y) * 58;
							delta_y = -58;
						else -- can only be the last case (quadrant with x<0 y<0)
							delta_x = math.cos(rad_angle) * 58;
							delta_y = math.sin(rad_angle) * 58;
						end;
					end;
				end;
				poi:SetPoint("CENTER", Minimap, "CENTER", delta_x, -delta_y);
				if (poi.icon ~= note.icon) then poi:SetIcon(note.icon); end;
				poi.title = note.title;
				poi:Show();
				if (not rad_angle) then
					-- is inside of the minimap, no arrow / other things needed, so just show it.
					if ( poi.isInArrowMode ) then
						getglobal("MininoteLibPOI"..next_poi.."Texture"):Show();
						getglobal("MininoteLibPOI"..next_poi.."Arrow"):Hide();
						poi.isInArrowMode = false;
					end;
				else
					-- use a light blue arrow by default ofr all kind of notes
					-- or a fade note (option)
					poi:Rotate(rad_angle);
					if ( not poi.isInArrowMode ) then
						getglobal("MininoteLibPOI"..next_poi.."Texture"):Hide();
						getglobal("MininoteLibPOI"..next_poi.."Arrow"):Show();
						poi.isInArrowMode = true;
					end;
				end;
				--DevTools_Dump("count_table "..count_table.." count_subtable "..count_subtable.." next_poi "..next_poi.." count_poi "..count_poi);
				--DevTools_Dump(note);
				count_subtable = count_subtable + 1;
				count_poi = count_poi + 1;
				next_poi = next_poi + 1;
				if (count_poi >= MininoteLib.MAX_NOTES_PER_FRAME) then return; end;
			end;
			count_table = count_table + 1;
			count_subtable = 1;
		end;
		-- start a new cycle (in this case there is at least one note left to update this frame):
		if (not cycle_restarted) then
			-- Hide all unsed POI's
			for i=next_poi, MininoteLib.NUM_POIS do
				getglobal("MininoteLibPOI"..i):Hide();
			end;
			-- check for start time, if not return...
			if ( start_time and start_time + MininoteLib.MIN_TIME_BETWEEN_CYCLES > GetTime() ) then return; end;
			start_time = GetTime();
			count_table = 1;
			count_subtable = 1;
			next_poi = 1;
			cycle_restarted = true;
		else
			cycle_complete = true;
		end;
	end;
end;

MininoteLib.XML.CreatePOI = function()
	MininoteLib.NUM_POIS = MininoteLib.NUM_POIS + 1;
	local frame = CreateFrame("Button", "MininoteLibPOI"..MininoteLib.NUM_POIS, Minimap);
	frame:SetWidth(16);
	frame:SetHeight(16);
	-- if this is the best way, I dunno, and think about it later.
	frame:SetFrameLevel(3);
	local texture = frame:CreateTexture("MininoteLibPOI"..MininoteLib.NUM_POIS.."Texture");
	texture:SetHeight(16);
	texture:SetWidth(16);
	texture:SetAllPoints(true);
	texture:Show();
	texture:SetTexture("Interface\\AddOns\\MapNotes\\Icons\\Cross");
	-- /script MininoteLibPOI1:SetPoint("CENTER", Minimap, "CENTER", 0, 0);
	local texture = frame:CreateTexture("MininoteLibPOI"..MininoteLib.NUM_POIS.."Arrow");
	texture:SetHeight(32);
	texture:SetWidth(32);
	texture:Hide();
	texture:SetPoint("CENTER", frame, "CENTER", 0, 0);
	texture:SetTexture(MininoteLib.PATH.."Rotating-MinimapArrow");
	frame.Rotate = MininoteLib.XML.RotateDummyFunction;
	frame.SetIcon = MininoteLib.XML.SetIconDummyFunction;
	frame.isInArrowMode = false;
	frame:SetToplevel(true);
	frame:SetFrameStrata("DIALOG");
	frame:SetScript("OnEnter", function(this) GameTooltip:SetOwner(this, "ANCHOR_CURSOR"); GameTooltip:SetText(this.title); end);
	frame:SetScript("OnLeave", function(this) GameTooltip:Hide(); end);
--~ 	frame:EnableMouse(true); Well thanks Cide for pointing this out but I did need a button for the ping now anyway ;)
	frame:SetScript("OnClick", function(this, button)
		-- function changed to be able to ping through the MiniNote
		-- (mostly direct copy) (only change: this -> Minimap)
		local x, y = GetCursorPosition();
		x = x / Minimap:GetEffectiveScale();
		y = y / Minimap:GetEffectiveScale();
		local cx, cy = Minimap:GetCenter();
		x = x + CURSOR_OFFSET_X - cx;
		y = y + CURSOR_OFFSET_Y - cy;
		if ( sqrt(x * x + y * y) < (Minimap:GetWidth() / 2) ) then
			Minimap:PingLocation(x, y);
		end;
	end);
end;

local square_half, rad_45 = sqrt(0.5), math.rad(45);
MininoteLib.XML.RotateDummyFunction = function(this, rad_angle)
	-- because the rotation is 45 deg off (draw a circle and a square and you might understand :) )
	rad_angle = rad_angle - rad_45;
	local sin, cos = math.sin(-rad_angle) * square_half, math.cos(-rad_angle) * square_half;
	getglobal(this:GetName().."Arrow"):SetTexCoord(0.5-sin, 0.5+cos, 0.5+cos, 0.5+sin, 0.5-cos, 0.5-sin, 0.5+sin, 0.5-cos);
end;

MininoteLib.XML.SetIconDummyFunction = function(this, icon)
	-- set the normal texture and frame size (max 24).
	local texture = getglobal(this:GetName().."Texture");
	texture:SetTexture(icon.file);
	if (icon.size) then
		this:SetWidth(math.min(icon.size, MininoteLib.MAX_ICON_MININOTE_SIZE));
		this:SetHeight(math.min(icon.size, MininoteLib.MAX_ICON_MININOTE_SIZE));
	else
		this:SetWidth(16);
		this:SetHeight(16);
	end;
	if (icon.tcoordMinX) then texture:SetTexCoord(icon.tcoordMinX, icon.tcoordMaxX, icon.tcoordMinY, icon.tcoordMaxY); else texture:SetTexCoord(0, 1, 0, 1); end;
	if (icon.colorR) then texture:SetVertexColor(icon.colorR, icon.colorG, icon.colorB, icon.colorA); else texture:SetVertexColor(1, 1, 1, 1); end;
	-- set the arrow texture
	texture = getglobal(this:GetName().."Arrow");
	if (icon.arrowFile) then texture:SetTexture(icon.arrowFile); else texture:SetTexture(MininoteLib.PATH.."Rotating-MinimapArrow"); end;
	if (icon.arrowColorR) then texture:SetVertexColor(icon.arrowColorR, icon.arrowColorG, icon.arrowColorB, icon.arrowColorA); else texture:SetVertexColor(1, 1, 1, 1); end;
	this.icon = icon;
end;

MininoteLib.XML.CreateOnUpdateFrame = function()
	local f = CreateFrame("Frame", "MininoteLibOnUpdateFrame", Minimap);
	f:SetWidth(1); f:SetHeight(1);
	f:SetPoint("CENTER", Minimap, "CENTER", 0, 0);
	f:SetScript("OnUpdate", MininoteLib.OnUpdate);
	f:Hide();
end;

-- mininote_table:
-- {
--   {title, icon, x, y}
-- }
-- title and icon are obvious =)
-- x,y is the corrdiantes on the continent map


-- /script MininoteLib.Register({ {title = "Hallo", icon = MapNotes.Icon.GetIconTexture(1), x = 0.4823, y = 0.5145}   })
-- /script f = CreateFrame("Frame", "Moritz", UIParent); f:SetWidth(1); f:SetHeight(1); f:SetPoint("CENTER", UIParent, "CENTER", 0, 0); f:SetScript("OnUpdate", MininoteLib.OnUpdate); f:Show();
MininoteLib.Register = function(mininote_table, is_static)
	-- Check if that table is already registered, if thats the case, skip
	for _, v in ipairs(MininoteLib.registeredTables) do
		if (v == table) then return; end;
	end;
	-- is static is used to make sure to have those tables with more
	-- static (only changes on zone change / user action) notes in
	-- the beginning and don't have to update textures and sizes that often.
	if (is_static) then 
		table.insert(MininoteLib.registeredTables, 1, mininote_table);
	else
		table.insert(MininoteLib.registeredTables, mininote_table);
	end;
end;

-- Mininote.Event:RegisterEvent
-- Mininote.Event.ON_ZONE_CHANGED
-- MininoteLib.Event.Mininote_ChangedNotes

MininoteLib.OnLoad = function()
	MapLib.EventHandler.RegisterEvent("MININOTELIB", "MINIMAP_UPDATE_ZOOM", MininoteLib.Event.MINIMAP_UPDATE_ZOOM);
	MapLib.EventHandler.RegisterEvent("MININOTELIB", "MININOTELIB_INFORM_TABLE_CHANGED", MininoteLib.Event.MININOTELIB_INFORM_TABLE_CHANGED);
	MapLib.EventHandler.RegisterEvent("MININOTELIB", "ZONE_CHANGED_NEW_AREA", MininoteLib.Event.ZONE_CHANGED_NEW_AREA);
	MapLib.EventHandler.RegisterEvent("MININOTELIB", "PLAYER_ENTERING_WORLD", MininoteLib.Event.PLAYER_ENTERING_WORLD);
	-- MININOTELIB_ZONE_CHANGED
	MininoteLib.XML.CreateOnUpdateFrame();
end;

MininoteLib.Event.ZONE_CHANGED_NEW_AREA = function()
	if (not MapLibFrame:IsVisible() ) then
		SetMapToCurrentZone();
	end;
	MapLib.EventHandler.TriggerEvent("MININOTELIB_ZONE_CHANGED", MapLib.GetMapIDByName(GetRealZoneText()));
	MapLib.EventHandler.TriggerEvent("MININOTELIB_INFORM_TABLE_CHANGED");
end;

MininoteLib.Event.PLAYER_ENTERING_WORLD = function()
	-- on PEW GetRealZoneText is not set.
	Chronos.schedule(1, MininoteLib.Event.ZONE_CHANGED_NEW_AREA);
end;

MininoteLib.Event.MINIMAP_UPDATE_ZOOM = function()
	local tempzoom = 0;
	if (GetCVar("minimapZoom") == GetCVar("minimapInsideZoom")) then
		if (tonumber(GetCVar("minimapInsideZoom")) >= 3) then
			Minimap:SetZoom(Minimap:GetZoom() - 1);
			tempzoom = 1;
		else
			Minimap:SetZoom(Minimap:GetZoom() + 1);
			tempzoom = -1;
		end
	end
	if (tonumber(GetCVar("minimapInsideZoom")) == Minimap:GetZoom()) then
		MininoteLib.isInside = true;
	else
		MininoteLib.isInside = false;
	end
	Minimap:SetZoom(Minimap:GetZoom() + tempzoom);	
end;

MininoteLib.Event.MININOTELIB_INFORM_TABLE_CHANGED = function()
	-- check if the onupdate frame needs to be shown or not.
	-- in case we don't need it, make sure all POI's are hidden.
	for _, v in ipairs(MininoteLib.registeredTables) do
		if (table.maxn(v) > 0) then
			-- reset teh cycle? I dunno...
			-- start_time = GetTime();
			-- count_table = 1;
			-- count_subtable = 1;
			-- next_poi = 1;
			MininoteLibOnUpdateFrame:Show();
			return;
		end;
	end;
	MininoteLibOnUpdateFrame:Hide();
	for i=1, MininoteLib.NUM_POIS do
		getglobal("MininoteLibPOI"..i):Hide();
	end;
end;

MininoteLib.Conversion["CO_EK"] = {
	[0] = { xscale = 11176.0, yscale = 7565.3 },
	[1] = { xscale = 13007.4, yscale = 8768.7 },
	[2] = { xscale = 15727.1, yscale = 10509.3 },
	[3] = { xscale = 19861.9, yscale = 13340.8 },
	[4] = { xscale = 26089.0, yscale = 16717.0 },
	[5] = { xscale = 39590.0, yscale = 26880.9 },
};
MininoteLib.Conversion["CO_KA"] = {
	[0] = { xscale = 11016.6, yscale = 7399.9 },
	[1] = { xscale = 12897.3, yscale = 8638.1 },
	[2] = { xscale = 15478.8, yscale = 10368.0 },
	[3] = { xscale = 19321.8, yscale = 12992.7 },
	[4] = { xscale = 25650.4, yscale = 17253.2 },
	[5] = { xscale = 38787.7, yscale = 26032.1 },
};
-- I honestly don't know why the following values refused to work
-- in the case I find it out, I'd like to still have them around.
--MininoteLib.Conversion["CO_OL"] = {
--	[0] = { xscale = 4944.8, yscale = 3291.9 },
--	[1] = { xscale = 5765.0, yscale = 3847.8 },
--	[2] = { xscale = 6896.7, yscale = 4603.4 },
--	[3] = { xscale = 8678.2, yscale = 5766.6 },
--	[4] = { xscale = 11542.1, yscale = 7674.3 },
--	[5] = { xscale = 17326.8, yscale = 11520.0 },
--};
MininoteLib.Conversion["CO_OL"] = {
	[0] = { xscale = 5233.5, yscale = 3484.1 },
	[1] = { xscale = 6101.6, yscale = 4072.4 },
	[2] = { xscale = 7299.3, yscale = 4872.1 },
	[3] = { xscale = 9184.8, yscale = 6103.2 },
	[4] = { xscale = 12215.9, yscale = 8122.3 },
	[5] = { xscale = 18338.3, yscale = 12192.5 },
};
MininoteLib.Conversion["isInside"] = {
	[0] = 1.565,
	[1] = 1.687,
	[2] = 1.882,
	[3] = 2.210,
	[4] = 2.575,
	[5] = 2.651,
};

MininoteLib.OnLoad();